/*  iImager (network disk imaging via iscsi)
        Copyright (C) 2009-2010 Yao Wei <njustyw@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "lib_imaging.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <mysql.h>

#include <QtGui>
#include "Q_LogDB.h"
#include "trimps_forensic_global.h"


#ifdef Q_OS_WIN
#define myatoi _atoi64
#define I64D	"I64d"
#define I64U	"I64u"
#define I64X	"I64x"
#else
#define myatoi atoll
#define I64D	"lld"
#define I64U	"llu"
#define I64X	"llx"
#endif

#define SQL_MAXLEN 1024*3

#define LOG_ADD_NEW_DISK		0
#define LOG_REMOVE_DISK			1
#define LOG_READY_COPY_DISK		2
#define LOG_START_COPY_DISK		3
#define LOG_FINISH_COPY_DISK            4
#define LOG_DROP_COPY_DISK		5
#define LOG_RESUME_COPY_DISK            6
#define LOG_ERROR_HAPPEN		7
#define LOG_POWERON                     8
#define LOG_SHUTDOWN			9
#define LOG_CONFIGURE			10

#define NOITEM				0

#define LOGINFO			"forensic_log"
#define DISKINFO		"forensic_diskinfo"
#define DISKLOCINFO		"forensic_disklocation"
#define RUNNINGINFO		"forensic_runninginfo"
#define USERINFO		"t_user"

#define L32(v) 	(unsigned int)(v & 0x0FFFFFFFFU)
#define H32(v)	(unsigned int)(v >> 32)
#define LH64(l, h)	(quint64)((quint64)l | ((quint64)h << 32))

static	MYSQL* conn;

static bool conn_status = false;

static int mysql_exec(MYSQL *mysql, const char *q)						
{													
	int r = mysql_query(mysql, q);					
	mysql_free_result(mysql_store_result(mysql));	
	return r;										
}


static bool search_from_diskinfo(const char* product, const char* vendor, const char* sn,
								 quint64 capcity, int& hdId)
{
	char sql[SQL_MAXLEN];
    
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select * from %s where product=\'%s\' and vendor=\'%s\' and sn=\'%s\' "
            "and lcapcity=%u and hcapcity=%u",
            DISKINFO, product, vendor, sn, L32(capcity), H32(capcity));
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  

	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
		hdId = NOITEM;
    else
	{
       MYSQL_ROW row = mysql_fetch_row(result);
	   hdId = atoi(row[0]);
	   mysql_free_result(result);
	}
	
	return true;
}

static bool insert_into_diskinfo(const char* product, const char* vendor, const char* sn,
                                                                quint64 capcity)
{
	char sql[SQL_MAXLEN];
	memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "insert into %s values(NULL, \'%s\', \'%s\', \'%s\', %u, %u)",
            DISKINFO, vendor, product, sn, L32(capcity), H32(capcity));
	return !mysql_exec(conn, sql);
}

static bool insert_into_user(const char* userid, const char* passwd, int type)
{
	char sql[SQL_MAXLEN];
	memset(sql, 0, SQL_MAXLEN);
	sprintf(sql,
		"insert into %s values(\'%s\', \'%s\', \'%s\', \'%s\', 15, 0, \'%s\', %d)",
		USERINFO, userid, userid, passwd, userid, "", type);
	return !mysql_exec(conn, sql);
}

static bool get_diskinfo(int hdId, QString& vendor, QString& product, QString& sn,
                                                quint64& capcity)
{
    char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select * from %s where hdId=%d",
            DISKINFO, hdId);
	
	if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  

	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
    {
        vendor = "";
        product = "";
        sn = "";
        capcity = 0;
    }
    else
    {
        MYSQL_ROW row = mysql_fetch_row(result);
		vendor = row[1];
        product = row[2];
        sn = row[3];
        capcity = LH64(myatoi(row[4]), myatoi(row[5]));
        mysql_free_result(result);
    }
	return true;
}

static bool update_diskinfo(int hdId, int hdlocId)
{
	char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "update %s set hdlocId=%d where hdId=%d",
            DISKINFO, hdlocId, hdId);
    return !mysql_exec(conn, sql);
}

static bool search_from_diskloc(int hdId, int rackIndex, int slotIndex,
                                                           int& hdlocId)
{
    char sql[SQL_MAXLEN];
        
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select hdlocId from %s where hdId=%d and rack_loc=%d and slot_loc=%d",
            DISKLOCINFO, hdId, rackIndex, slotIndex);
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
    {
		hdlocId = NOITEM;
    }
    else
    {
        MYSQL_ROW row = mysql_fetch_row(result);
		hdlocId = atoi(row[0]);
        mysql_free_result(result);
    }
	return true;
}

static bool insert_into_diskloc(int hdId, int rackIndex, int slotIndex)
{
    char sql[SQL_MAXLEN];
    int status;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "insert into %s values(NULL, %d, %d, %d)",
            DISKLOCINFO, hdId, rackIndex, slotIndex);
    return !mysql_exec(conn, sql);
}

static bool get_diskloc(int hdlocId, int& rackIndex, int& slotIndex)
{
    char sql[SQL_MAXLEN];
    
    rackIndex = -1;
    slotIndex = -1;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select * from %s where hdlocId=%d",
            DISKLOCINFO, hdlocId);
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
    {
        rackIndex = -1;
        slotIndex = -1;
    }
    else
    {
        MYSQL_ROW row = mysql_fetch_row(result);
        rackIndex = atoi(row[2]);
        slotIndex = atoi(row[3]);
        mysql_free_result(result);
    }
	return true;
}

static bool insert_into_runninginfo(int hdId, int r_status, const char* imgingname, int imagingFormat, int hashType, const char* caseNo)
{
    char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql, "insert into %s values(NULL, now(), %d, %d, \'%s\', %d, %d, \'%s\')",
            RUNNINGINFO, hdId, r_status, imgingname, imagingFormat, hashType, caseNo);

    return !mysql_exec(conn, sql);
}

static bool search_from_runninginfo(int hdId, int r_status, const char* imgingname, int imagingFormat, int hashType, const char* caseNo, int& rId)
{
	char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select rId from %s where hdId=%d and r_status=%d and filename=\'%s\' and imaging_type=%d and hash_type=%d and case_no=\'%s\'",
            RUNNINGINFO, hdId, r_status, imgingname, imagingFormat, hashType, caseNo);
    //qDebug() << sql;
	if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
    if(nrow == 0)
	{
		rId = NOITEM;
	}
    else
    {
		MYSQL_ROW row = mysql_fetch_row(result);
		rId = atoi(row[0]);
		mysql_free_result(result);
    }
	return true;
}

static bool search_from_log(int hdId, int hdlocId, int msg_type, int& logId, const char* msg = "", int rId = 0)
{
    char sql[SQL_MAXLEN];
    
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select logId from %s where hdId=%d and hdlocId=%d and msg_type=%d and msg=\'%s\' and rId=%d",
            LOGINFO, hdId, hdlocId, msg_type, msg, rId);
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
	if(nrow == 0)
	{
		logId = NOITEM;
	}
	else
    {
		MYSQL_ROW row = mysql_fetch_row(result);
        logId = atoi(row[0]);
        mysql_free_result(result);
    }
    return true;
}

static bool get_running_format(int rId, int &imaging_type, int& hash_type, char* case_no)
{
    char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select imaging_type, hash_type, case_no from %s where rId=%d",
            RUNNINGINFO, rId);
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
	if(nrow == 0)
		return false;

	MYSQL_ROW row = mysql_fetch_row(result);
	imaging_type = atoi(row[0]);
    hash_type = atoi(row[1]);
    strcmp(case_no, row[2]);

    mysql_free_result(result);

    return true;
}

static bool get_running_filename(int rId, char filename[])
{
    char sql[SQL_MAXLEN];

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select filename from %s where rId=%d",
            RUNNINGINFO, rId);
    if(mysql_query(conn, sql) != 0)
		return false;

	MYSQL_RES* result = mysql_store_result(conn);  
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
	if(nrow == 0)
		return false;

	MYSQL_ROW row = mysql_fetch_row(result);
	strcpy(filename, row[0]);
    mysql_free_result(result);

    return true;
}

LogInsertEvent* LogInsertEvent::instance = 0;

LogInsertEvent* LogInsertEvent::getInstance()
{
    if(instance == 0) instance = new LogInsertEvent();
    return instance;
}

LogInsertEvent::LogInsertEvent(QObject* parent /* = 0 */):QObject(parent)
{

}

LogInsertEvent::~LogInsertEvent()
{

}

static LogInsertEvent* mysignal = 0;

LogDB* LogDB::pInstance = 0;

static bool insertLog(int hdId, int hdlocId, int type, const char* msg  = "", int rId  = 0 )
{
	if(!conn_status)	
		return false;

	char sql[SQL_MAXLEN];

	memset(sql, 0, SQL_MAXLEN);
	sprintf(sql,
		"insert into %s values(NULL, now(), %d, %d, %d, \"%s\", %d)",
		LOGINFO, hdId, hdlocId, type, msg, rId);
	if(mysql_exec(conn, sql) != 0)
		return false;

	//if(type != LOG_POWERON || type != LOG_SHUTDOWN)
	//	emit mysignal->signal_insertSucceed();
	return true;
}


static QString gDatabase, gHost, gUsername, gPassword;

LogDB::LogDB(QObject* parent /* = 0 */):QObject(parent)
{
    setObjectName("logDB");
}

LogDB* LogDB::instance(const QString& database, const QString& host,
					   const QString& username, const QString& password)
{
    if(pInstance == 0)
	{
		gDatabase = database;
		gHost = host;
		gPassword = password;
		gUsername = username;
		pInstance = new LogDB();
	}

    return pInstance;
}

LogDB* LogDB::instance()
{
	return pInstance;
}

bool LogDB::isConnected()const
{
	return conn_status;
}


bool LogDB::initDB()
{
	char sql[SQL_MAXLEN];

	conn_status = false;
	error = UNKNOWN_ERROR;
	conn = mysql_init(NULL);
	if(conn == NULL)	
		return false;

	if(mysql_options(conn, MYSQL_SET_CHARSET_NAME, "utf8") != 0)
		return false;
	
	if(mysql_real_connect(conn, gHost.toUtf8().data(), gUsername.toUtf8().data(), 
		gPassword.toUtf8().data(), gDatabase.toUtf8().data(),	MYSQL_PORT, NULL, 0) == NULL)
	{
		conn_status = false;
		error = CONNECTION_ERROR;
		return false;
	}
	conn_status = true;

    // Are there DBTABLE ? if not, create it.
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql, "select * from %s limit 1", LOGINFO);
	if(mysql_exec(conn, sql) != 0)
	{
		memset(sql, 0, SQL_MAXLEN);
		sprintf(sql,
			"create table %s(logId INTEGER PRIMARY KEY AUTO_INCREMENT, time TEXT, "
			"hdId INTEGER, hdlocId INTEGER, msg_type INTEGER, msg TEXT, rId INTEGER)"
			"ENGINE=InnoDB DEFAULT CHARSET=utf8",
			LOGINFO);
		if(mysql_exec(conn, sql) != 0)
		{
			error = SQL_ERROR;
			return false;
		}
	}
	
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql, "select * from %s limit 1", DISKINFO);
    if(mysql_exec(conn, sql) != 0)
	{
		memset(sql, 0, SQL_MAXLEN);
        sprintf(sql,
                "create table %s(hdId INTEGER PRIMARY KEY AUTO_INCREMENT, vendor TEXT, "
                "product TEXT, sn TEXT, lcapcity INTEGER unsigned DEFAULT NULL, hcapcity INTEGER unsigned DEFAULT NULL)"
				"ENGINE=InnoDB DEFAULT CHARSET=utf8",				
                DISKINFO);
        if(mysql_exec(conn, sql) != 0)
		{
			error = SQL_ERROR;
			return false;
		}
	}

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql, "select * from %s limit 1", DISKLOCINFO);
    if(mysql_exec(conn, sql) != 0)
	{
		memset(sql, 0, SQL_MAXLEN);
        sprintf(sql,
                "create table %s(hdlocId INTEGER PRIMARY KEY AUTO_INCREMENT, hdId INTEGER, "
				"rack_loc INTEGER, slot_loc INTEGER)"
				"ENGINE=InnoDB DEFAULT CHARSET=utf8",
                DISKLOCINFO);
        if(mysql_exec(conn, sql) != 0)
		{
			error = SQL_ERROR;
			return false;
		}
	}

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql, "select * from %s limit 1", RUNNINGINFO);
    if(mysql_exec(conn, sql) != 0)
	{
		memset(sql, 0, SQL_MAXLEN);
        sprintf(sql,
                "create table %s(rId INTEGER PRIMARY KEY AUTO_INCREMENT, time TEXT, hdId INTEGER, r_status INTEGER, "
                "filename TEXT, imaging_type INTEGER, hash_type INTEGER, case_no TEXT)"
				"ENGINE=InnoDB DEFAULT CHARSET=utf8",
                RUNNINGINFO);
        if(mysql_exec(conn, sql) != 0)
		{
			error = SQL_ERROR;
			return false;
		}
	}

    mysignal = LogInsertEvent::getInstance();
    return true;
}

void LogDB::closeDB()
{
   	DEBUG_MSG("Enter");
	mysql_close(conn);
}

bool LogDB::addSystemOnLog()
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;

	if(!insertLog(-1, -1, LOG_POWERON))
	{
		error = SQL_ERROR;
		return false;
	}

	return true;
}

bool LogDB::addSystemOffLog()
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	if(!insertLog(-1, -1, LOG_SHUTDOWN))
	{
		error = SQL_ERROR;
		return false;
	}
	
	return true;
}

bool LogDB::addSystemConfigLog(const QString& msg, int type)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	if(!insertLog(type, -1, LOG_CONFIGURE, msg.toUtf8().data()))
	{
		error = SQL_ERROR;
		return false;
	}
	
	return true;
}

bool LogDB::addDiskFoundLog(const QString& _product, const QString& _vendor, const QString& _sn,
                                           quint64 capcity, int rackIndex, int slotIndex,
                                           int& hdId, int& hdlocId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

    error = UNKNOWN_ERROR;
	int logId;

    // We firstly scan from the database "diskinfo" when a new disk added
    // If the disk item has not been recorded, we insert it into the db
    bool status = search_from_diskinfo(_product.toUtf8().data(), _vendor.toUtf8().data(), _sn.toUtf8().data(), capcity, hdId);
	if(!status)
	{
		error = SQL_ERROR;
		return false;
	}

    if(hdId == NOITEM)
    {
		status = insert_into_diskinfo(_product.toUtf8().data(), _vendor.toUtf8().data(), _sn.toUtf8().data(), capcity);
        if(!status)
		{
			error = SQL_ERROR;
			return false;
		}

        status = search_from_diskinfo(_product.toUtf8().data(), _vendor.toUtf8().data(), _sn.toUtf8().data(), capcity, hdId);
		if(!status)
		{
			error = SQL_ERROR;
			return false;
		}
	}

    // If the disk item has already been recorded, we scan the db "disklocation"
    status = search_from_diskloc(hdId, rackIndex, slotIndex, hdlocId);
    if (!status)
	{
		error = SQL_ERROR;
		return false;
	}

    if(hdlocId == NOITEM)
    {
        status = insert_into_diskloc(hdId, rackIndex, slotIndex);
        if(!status)
		{
			error = SQL_ERROR;
			return false;
		}

        status = search_from_diskloc(hdId, rackIndex, slotIndex, hdlocId);
        if(!status)
		{
			error = SQL_ERROR;
			return false;
		}
    }
	
	status = search_from_log(hdId, hdlocId, LOG_ADD_NEW_DISK, logId);
    if(!status)
	{
		error = SQL_ERROR;
		return false;
	}

    if(logId == NOITEM)
	{
		if(!insertLog(hdId, hdlocId, LOG_ADD_NEW_DISK))
		{
			error = SQL_ERROR;
			return false;
		}
		return true;
	}

    return true;
}

bool LogDB::addDiskReadyForCopyLog(int hdId, int hdlocId, const QString& imgingName, int imagingFormat,
                                   int hashType, const QString& caseNo, int& rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	int logId;
    int logType = LOG_READY_COPY_DISK;
    int runStatus = READYFOR_COPY_DISK;
	
	bool status = search_from_runninginfo(hdId, runStatus, imgingName.toUtf8().data(), imagingFormat, hashType, caseNo.toUtf8().data(), rId);
	if(!status)
	{
		error = SQL_ERROR;
		return false;
	}

    if(rId == NOITEM)
    {
        runStatus = DOING_COPY_DISK;
        status = search_from_runninginfo(hdId, runStatus, imgingName.toUtf8().data(), imagingFormat, hashType, caseNo.toUtf8().data(), rId);
		if(!status)
		{
			error = SQL_ERROR;
			return false;
		}
    }

    if(rId == NOITEM)
    {
        int runStatus = READYFOR_COPY_DISK;
        status = insert_into_runninginfo(hdId, runStatus, imgingName.toUtf8().data(), imagingFormat, hashType, caseNo.toUtf8().data());
		if(!status)
		{
			error = SQL_ERROR;
			return false;
		}

        status = search_from_runninginfo(hdId, runStatus, imgingName.toUtf8().data(), imagingFormat, hashType, caseNo.toUtf8().data(), rId);
		if(!status)
		{
			error = SQL_ERROR;
			return false;
		}
    }
    else
		logType = RESUME_COPY_DISK;

    // 	status = search_from_log(hdId, hdlocId, LOG_READY_COPY_DISK, logId, "", rId);
    // 	if(!status)
    // 	{
    // 		return false;
    // 	}
    // 	if(logId == NOITEM)
    {
        if(!insertLog(hdId, hdlocId, logType, "", rId))
		{
			error = SQL_ERROR;
			return false;
		}
		return true;
    }

    return true;
}

bool LogDB::addDiskStartCopyLog(int hdId, int hdlocId, int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	if(!insertLog(hdId, hdlocId, LOG_START_COPY_DISK, "", rId))
	{
		error = SQL_ERROR;
		return false;
	}
	
	return true;
}

bool LogDB::addDiskDoneCopyLog(int hdId, int hdlocId, int rId, double meanSpeed, int totalTime)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;
	int hash_type, imaging_type;
    char case_no[1024];
    const char* text = "";

    bool status = get_running_format(rId, imaging_type, hash_type, case_no);
    if(status)
    {
        QString hh;
        if(hash_type & HASH_MD5) hh += tr("MD5 ");
        if(hash_type & HASH_SHA128) hh += tr("SHA1 ");
        if(hash_type & HASH_SHA256) hh += tr("SHA256");

        QString Msg = tr("The time cost is %1 min %2 sec, average speed is %3 GiB/min and hash_method is %4").
        arg(totalTime/60).arg(totalTime%60).arg(meanSpeed, 0, 'g', 3).arg(hh);

        text = Msg.toUtf8().data();
    }

	if(!insertLog(hdId, hdlocId, LOG_FINISH_COPY_DISK, text, rId))
	{
		error = SQL_ERROR;
		return false;
	}
	
	return true;
}

bool LogDB::addDiskDropCopyLog(int hdId, int hdlocId, int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	if(!insertLog(hdId, hdlocId, LOG_DROP_COPY_DISK, "", rId))
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::addExceptionCopyLog(int hdId, int hdlocId, const QString& msg, int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	const char* text = msg.toUtf8().data();
    if(!insertLog(hdId, hdlocId, LOG_ERROR_HAPPEN, text, rId))
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::addDiskRemoveLog(int hdId, int hdlocId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	if(!insertLog(hdId, hdlocId, LOG_REMOVE_DISK))
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::startRunning(int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;
	char sql[SQL_MAXLEN];
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "update %s set r_status=%d where rId=%d",
            RUNNINGINFO, DOING_COPY_DISK, rId);
    
	int r = mysql_exec(conn, sql);
	if(r != 0)
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::doneRunning(int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	char sql[SQL_MAXLEN];
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "update %s set r_status=%d where rId=%d",
            RUNNINGINFO, DONE_COPY_DISK, rId);

    emit mysignal->signal_insertDoneTask();
	int r = mysql_exec(conn, sql);
	if(r != 0)
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::dropRunning(int rId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	char sql[SQL_MAXLEN];
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "update %s set r_status=%d where rId=%d",
            RUNNINGINFO, DROP_COPY_DISK, rId);
    int r = mysql_exec(conn, sql);
	if(r != 0)
	{
		error = SQL_ERROR;
		return false;
	}
	return true;
}

bool LogDB::addUserInfo(const QString& username, const QString& password, int userType)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;


}

bool LogDB::selectLogMsg(int pageCount, int startOffset, QList<QString>& msgs)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;
	char sql[SQL_MAXLEN];
    QString oneMsg;
    int msgType;
    QString vendor, product, sn;
    quint64 capcity;
    int slotIndex, rackIndex;
    int hdId, hdlocId, rId;
    unsigned int capGB;
    QString capStr;
    char fileName[1024], case_no[1024];
    int imaging_type, hash_type;
	bool status;

    msgs.clear();
    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select time, hdId, hdlocId, msg_type, msg, rId from %s order by time desc limit %d offset %d",
            LOGINFO, pageCount, startOffset);
    if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return false;
	}

	MYSQL_RES* result = mysql_store_result(conn);
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
    for(int i = 0; i < nrow; i++)
    {
		MYSQL_ROW row = mysql_fetch_row(result);

        hdId = atoi(row[1]);
        hdlocId = atoi(row[2]);
        msgType = atoi(row[3]);
        rId = atoi(row[5]);

        if(msgType < LOG_POWERON)
        {
                status = get_diskloc(hdlocId, rackIndex, slotIndex);
                if(!status)	continue;
        }

        switch(msgType)
        {
        case LOG_ADD_NEW_DISK:
                status = get_diskinfo(hdId, vendor, product, sn, capcity);
                if(!status)	continue;
                capGB = capcity / 1000000;
                if(capGB < 1000)	capStr = QString::number(capGB) + "MiB";
                else				capStr = QString::number(capGB/1000) + "GiB";

                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th location comes out a new disk (VendorID['%4'], ProductID['%5'], Serial-Number['%6'], Capcity[%7])")
                        .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1).arg(vendor).arg(product).arg(sn).arg(capStr);
                break;

        case LOG_REMOVE_DISK:
                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk is being pulled out").arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1);
                break;

        case LOG_READY_COPY_DISK:
                status = get_running_format(rId, imaging_type, hash_type, case_no);
                if(status){
                        QString ff = (imaging_type == IMAGING_DD) ? "DD" : "E01";
                        QString hh;
                        if(hash_type & HASH_MD5) hh += tr("MD5 ");
                        if(hash_type & HASH_SHA128) hh += tr("SHA1 ");
                        if(hash_type & HASH_SHA256) hh += tr("SHA256");
                        oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk is added to imaging queue, the format of imaging is %4 and hash type is %5")
                                .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1).arg(ff).arg(hh);
                }
                break;

        case LOG_RESUME_COPY_DISK:
                status = get_running_format(rId, imaging_type, hash_type, case_no);
                if(status){
                        oneMsg = tr("Time: %1  Event: we continue imaging the %2-th rack, the %3-th disk that hasn't finished since last time")
                                .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1);
                }
                break;

        case LOG_START_COPY_DISK:
                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk starts for imaging").arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1);
                break;

        case LOG_FINISH_COPY_DISK:
                status = get_running_format(rId, imaging_type, hash_type, case_no);
                if(status)
                {
                        status = get_running_filename(rId, fileName);
                        if(status)
                        {
                                QString ff = (imaging_type == IMAGING_DD) ? ".DD" : ".E01";
                                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk completes imaging. %4. The imager file is stored in [\'%5%6\']")
                                        .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1).arg(tr(row[4])).arg(fileName).arg(ff);
                        }
                }
                else
                        oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk completes imaging and %4")
                        .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1).arg(tr(row[4]));
                break;

        case LOG_DROP_COPY_DISK:
                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk cancels imaging")
                        .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1);
                break;

        case LOG_ERROR_HAPPEN:
                oneMsg = tr("Time: %1  Event: the %2-th rack, the %3-th disk occurs something error during the process of imaging, since %4")
                        .arg(row[i*0]).arg(rackIndex+1).arg(slotIndex+1).arg(tr(row[4]));
                break;

        case LOG_POWERON:
                oneMsg = tr("Time: %1  Event: the system starts").arg(row[i*0]);
                break;

        case LOG_SHUTDOWN:
                oneMsg = tr("Time: %1  Event: the system exits").arg(row[i*0]);
                break;

        case LOG_CONFIGURE:
                if(hdId == 0)
                        oneMsg = tr("Time: %1  Event: read from configuration %2").arg(row[i*0]).arg(tr(row[4]));
                else
                        oneMsg = tr("Time: %1  Event: Re-configure the program %2").arg(row[i*0]).arg(tr(row[4]));
        }
        msgs.push_back(oneMsg);
    }

	mysql_free_result(result);

    return true;
}

int LogDB::getMsgCount()
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;
    char sql[SQL_MAXLEN];
    int count;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select count(*) from %s",
            LOGINFO);
    if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return -1;
	}

	MYSQL_RES* result = mysql_store_result(conn);
	MYSQL_ROW row = mysql_fetch_row(result);
    count = atoi(row[0]);
    mysql_free_result(result);

    return count;
}

int LogDB::getUndoneTasks(QList<QString>& imagingOutputNames, QList<int>& imagingTypes,
                          QList<int>& hashTypes,  QList<QString>& caseNos,
                          QList<QString>& vendorIds, QList<QString>& productIds,
                          QList<QString>& SNs, QList<quint64>& mediaSizes)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;

	char sql[SQL_MAXLEN];
    int hdId;
    QString vendor, product, sn;
    quint64 capcity;
    int realCaseNo;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select hdId, filename, imaging_type, hash_type, case_no from %s where r_status=%d or r_status=%d",
            RUNNINGINFO,DOING_COPY_DISK, READYFOR_COPY_DISK);
	if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return -1;
	}

    imagingOutputNames.clear();
    imagingTypes.clear();
    hashTypes.clear();
    productIds.clear();
    vendorIds.clear();
    SNs.clear();
    mediaSizes.clear();
    caseNos.clear();

	MYSQL_RES* result = mysql_store_result(conn);
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
		return 0;
    else
    {
		for(int i = 0; i < nrow; i++)
		{
			MYSQL_ROW row = mysql_fetch_row(result);
			hdId = atoi(row[0]);

            if(!get_diskinfo(hdId, vendor, product, sn, capcity))
                    continue;

            vendorIds.push_back(vendor);
            productIds.push_back(product);
            SNs.push_back(sn);
            mediaSizes.push_back(capcity);
            imagingOutputNames.push_back(row[1]);
            imagingTypes.push_back(atoi(row[2]));
            hashTypes.push_back(atoi(row[3]));
            caseNos.push_back(row[4]);
		}
        mysql_free_result(result);

		qDebug() << nrow << "undone tasks" << caseNos << imagingOutputNames;
    }

    return imagingTypes.size();
}

int LogDB::removeAllUndoneTasks()
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;

	char sql[SQL_MAXLEN];
	memset(sql, 0, SQL_MAXLEN);
	sprintf(sql,
		"delete from %s where r_status=%d or r_status=%d",
		RUNNINGINFO,DOING_COPY_DISK, READYFOR_COPY_DISK);
	return mysql_exec(conn, sql);
}

int LogDB::getAllCaseNos(QList<QString>& caseNos)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;

	char sql[SQL_MAXLEN];
    int hdId;
    QString vendor, product, sn;
    quint64 capcity;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select case_no from %s", RUNNINGINFO);
    if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return -1;
	}

    caseNos.clear();

	MYSQL_RES* result = mysql_store_result(conn);
	//int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);
    if(nrow == 0)
            return 0;
    else
    {
        for(int i = 0; i < nrow; i++)
        {
			MYSQL_ROW row = mysql_fetch_row(result);
			if(!caseNos.contains(row[0]))
				caseNos.push_back(row[0]);
        }
		mysql_free_result(result);
    }

    return caseNos.size();
}

int LogDB::getAllDoneList(QList<QString>& imagingOutputNames, QList<int>& imagingTypes,
                                           QList<quint64>& mediaSizes, QList<int>& hashTypes,
                                           QList<QString>& caseNos, QList<int>& julianDays,
                                           int &maxRId)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}
	
	error = UNKNOWN_ERROR;

	char sql[SQL_MAXLEN];
    int status;
    int hdId;
    QString vendor, product, sn;
    quint64 capcity;
    int count = 0;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select hdId, filename, imaging_type, hash_type, case_no, rId, time from %s where r_status=%d and rId>%d order by time asc, rId asc",
            RUNNINGINFO,DONE_COPY_DISK, maxRId);
	if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return -1;
	}

    imagingOutputNames.clear();
    imagingTypes.clear();
    hashTypes.clear();
    mediaSizes.clear();
    caseNos.clear();

	MYSQL_RES* result = mysql_store_result(conn);
	//int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
        return 0;
	else
	{
        for(int i = 0; i < nrow; i++)
        {
			MYSQL_ROW row = mysql_fetch_row(result);

            int julianDay = atoi(row[5]);
            if(maxRId < julianDay)
				maxRId = julianDay;

            hdId = atoi(row[0]);
            status = get_diskinfo(hdId, vendor, product, sn, capcity);
            if(!status)	continue;

            QString fullName = (atoi(row[2]) == IMAGING_DD) ? QString(row[1]) + ".DD" : QString(row[1]) + ".E01";
            //if(!QFile::exists(fullName))
            //	continue;

            QString fileName = QFileInfo(row[1]).fileName();
            QStringList names = fileName.split(QChar('_'));
            int n = names.size();
            if(n < 3)
                julianDay = 0;
            else
            {
                int year, month, day;
                int id = names[n-1].indexOf(QChar('-'));
                QString d = names[n-1].left(id);
                int dlen = d.length();
                year = d.mid(0, dlen-4).toInt();
                month = d.mid(dlen-4, 2).toInt();
                day = d.mid(dlen-2, 2).toInt();
                julianDay = QDate(year, month, day).toJulianDay();
            }

            int whichLoc = 0;
            for(int k = 0; k < julianDays.size() ; k++)
            {
                if(julianDay > julianDays[i])
                {
                    whichLoc = k;
                    break;
                }
            }

            julianDays.push_back(julianDay);
            mediaSizes.push_back(capcity);
            imagingOutputNames.push_back(row[1]);
            imagingTypes.push_back(atoi(row[2]));
            hashTypes.push_back(atoi(row[3]));
            caseNos.push_back(row[4]);
			
			count++;
        }
        mysql_free_result(result);
	}
	return count;
}


int LogDB::getAllDoneListByCaseNo(const QString& caseNo, QList<QString>& imagingOutputNames, QList<int>& imagingTypes,
                                                           QList<quint64>& mediaSizes, QList<int>& hashTypes, QList<QString>& caseNos)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return -1;
	}
	
	error = UNKNOWN_ERROR;

	char sql[SQL_MAXLEN];
    int status;
    int hdId;
    QString vendor, product, sn;
    quint64 capcity;
    int count = 0;

    memset(sql, 0, SQL_MAXLEN);
    sprintf(sql,
            "select hdId, filename, imaging_type, hash_type, case_no, rId from %s where r_status=%d and case_no like \'%s\' order by rId asc",
            RUNNINGINFO,DONE_COPY_DISK, caseNo == "*" ? "%" : caseNo.toUtf8().data());
    if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return -1;
	}

    imagingOutputNames.clear();
    imagingTypes.clear();
    hashTypes.clear();
    mediaSizes.clear();
    caseNos.clear();

	MYSQL_RES* result = mysql_store_result(conn);
	//int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

    if(nrow == 0)
		return 0;
    else
	{
        for(int i = 0; i < nrow; i++)
        {
			MYSQL_ROW row = mysql_fetch_row(result);

            hdId = atoi(row[0]);
            status = get_diskinfo(hdId, vendor, product, sn, capcity);
            if(!status)	continue;

            QString fullName = (atoi(row[2]) == IMAGING_DD) ? QString(row[1]) + ".DD" : QString(row[1]) + ".E01";
            if(!QFile::exists(fullName))
                    continue;

            mediaSizes.push_back(capcity);
            imagingOutputNames.push_back(row[1]);
            imagingTypes.push_back(atoi(row[2]));
            hashTypes.push_back(atoi(row[3]));
            caseNos.push_back(row[4]);
            count ++;
        }

        mysql_free_result(result);
	}
	return count;
}


bool LogDB::verifyUserPassword(const QString& user, const QString& password, int& type)
{
	if(!conn_status)	
	{
		error = CONNECTION_ERROR;
		return false;
	}

	error = UNKNOWN_ERROR;
	char sql[SQL_MAXLEN];
	memset(sql, 0, SQL_MAXLEN);
	sprintf(sql,
		"select passwd, forensic_role from %s where userid=\'%s\'", USERINFO, user.toUtf8().data());
	if(mysql_query(conn, sql) != 0)
	{
		error = SQL_ERROR;
		return false;
	}

	MYSQL_RES* result = mysql_store_result(conn);
	int nfield = mysql_num_fields(result);
	int nrow = mysql_num_rows(result);

	// case 1: the user database does not contain the information for useId
	if(nrow == 0)
	{
		error = NODATA_ERROR;
		return false;
	}

	MYSQL_ROW row = mysql_fetch_row(result);
	QString passwdDB = QString::fromUtf8(row[0]);
	type = atoi(row[1]);

	// case 2: user id doesn't match password
	if(password != passwdDB)
		return false;

	// case 3: matches
	return true;
}